 
import { ABaseDecision } from "GameLoop/DecisionMaking/BaseDecision";
import { InnerMineEntity, OutMineEntity } from "GameLoop/WorldState/EntityManager/Entitys";
import { getRoleConfig } from "utils/constants/bodyconstant";
import { priority } from "utils/constants/definetypes";
import { BodyUtils } from "utils/creep/bodyutils";
import { Roles } from "utils/creep/setups";
import { PositionHelper } from "utils/position/PositionHelper";
import { UtilsHelper } from "utils/position/UtilsHelper";
import { generateUniqueId } from "utils/utils";

/**
 * 房间的生产物流。爬虫数量控制.采集和运输
 */
export class ProducerCreepDecision< T extends "KeepCreep"="KeepCreep"> extends ABaseDecision<T>
{
   
    public isExpired(event: KeepEvent, region: string): boolean 
    {
        return true;
    }
    // 生成房间统计信息
    private makeRoomStats(room:Required<Room>,energy:number,energycost:number,bodycost:number ,powerConfig:{power:Record<number,number>,radio:number,},porterRadio:number)
    {
        // 确定采集工和运输工的数量
        // 有路 1:2 无路1:1
    
        const spawn_count = room.controller.level<7?1:room.getEntity("SpawnEntity").spawnStructures().length * powerConfig.radio;
        let upgradeWork = room.getNodeMemory()["stats"]?.upgradeWork 
        
        const stats:RoomStats=
        {
            energyinCome: energy,
            bodycost: bodycost,
            upgradeWork: upgradeWork ? upgradeWork : 0,
            governable: {
                energy: energy - energycost,
                body: spawn_count * 300  - bodycost,
                
            },
            power:powerConfig.power,
            // resource: {
            //     shortage:{},
            //     surplus:{}
            // }
            bodyRadio:powerConfig.radio,
            porterRadio:porterRadio
        }
        
        room.getNodeMemory()["stats"]=stats;
    }
    
    private getPowerConfig(room:Required<Room>)
    {
         
        let radio = 1;
        const record:Record<number,number>={
            1:10.0/9,
            2:10.0/7,
            3:10.0/5,
            4:10.0/3.5,
            5:10.0/2,
        }
        // room.memory.
        let power:Record<number,number>={};
        for(let k of room.getNodeMemory().powerCreeps )
        {
            const powerCreep = Game.powerCreeps[k];
            if(!powerCreep.ticksToLive)
            {
                continue;
            }
            for(let p in powerCreep.powers)
            {
                if( !power[p] ||power[p]<powerCreep.powers[p].level)
                {
                    power[p]= powerCreep.powers[p].level;
                    if(parseInt(p) == PWR_OPERATE_SPAWN)
                    {
                        // power
                        // radio = record[power[p]];
                    }
                }
            }
        }
        const powerConfig:{power:Record<number,number>,radio:number}={
            power:power,
            radio:radio
        };
        return powerConfig
    }
    
    /**
     * 计算房间body 能量统计信息,统计信息计算错误。
     * @param roles 
     * @param level 
     * @param bodylimit 用于生产的body数量最多多少,这个限制应该根据spawn数量来计算
     * @returns 
     */
    private getStats(roles:[RoleName,
        BodyPartConstant,
        number][],
    level:number)
    {
        global.Log.Info(`统计参数: ${JSON.stringify(roles)}`);
        const ret:{
            role:RoleName,
            body:BodyItem,
            required:number,
            cost:number,
            bodyparts:BodyPartConstant[]
        }[]=[] ;
        let total=0;
        let bodytotal=0;
        for(const e of roles)
        {
            const bodyconfig =  getRoleConfig(e[0],level);
            const role_requires = BodyUtils.getCreepCount(bodyconfig.body,e[1],e[2]);
            const bodyparts =  BodyUtils.flatBodyParts(bodyconfig.body); // 
            const rolecost = BodyUtils.calculateSpawnEnergy(bodyparts);
            const role_energy_cost = rolecost*role_requires;
            ret.push({
                role:e[0],
                body:bodyconfig.body,
                required:role_requires,
                cost:role_energy_cost,
                bodyparts:bodyparts
            })
            total+=role_energy_cost;
            bodytotal+=BodyUtils.getBodyCountCost(bodyparts,role_requires);
        }
        return {totalcost:total,totalbody:bodytotal,config:ret}
        
    }
    
    private sortOutMines(mines:OutMineEntity[])
    {
        const group = _.groupBy(mines,e=>e.roomName);
        return _.sortBy(group,e=>
            {
                if(e[0].RoomType  == 6)
                {
                    // 中央房
                    if(e[0].lordRoom.controller.level<7)
                    {
                        // 不能采集
                        return 10000;
                    }
                    else
                    {

                        // 能采集
                        const allcarry = _.sum(e,e=>e.getCarryBodyNum());
                        if(UtilsHelper.isCenterRoom( e[0].roomName) == 2)
                        {
                            // 最中间的房
                            return allcarry/ e.length*0.7;
                        }
                        else 
                        {
                            return allcarry/ e.length*0.9;
                        }
                        
                    }
                }
                const allcarry = _.sum(e,e=>e.getCarryBodyNum());
                const avgcarry = allcarry /e.length ;
                return e.length ==1?avgcarry*1.5:avgcarry;
                // carry 越多越不划算
            })
    }
    /**
     * 
     * @param room 当前主房间
     * @param innerMine 内矿
     * @param outMine 外矿
     * @param bodycount_limit 可使用的body上限
     */
    private getPorterRadio(room:Required<Room>,innerMine:InnerMineEntity[],outMine:OutMineEntity[],bodycount_limit:number)
    {
        let sum_bodyCount = 1;
        // 内矿
        {
            let sum_work=0;
            let sum_carry=0;
            for(const e of innerMine)
            {
                 
                sum_work+= e.getWorkBodyNum();
                if(!e.Link)
                {
                    // 没有Link的才需要运输爬
                    sum_carry += e.getCarryBodyNum();
                }
                
            }
            const Status = this.getStats([["mine",WORK,sum_work],["porter",CARRY,sum_carry]],room.controller.level);

            sum_bodyCount+=Status.totalbody;
        }

        // 外矿
        {
            let sum_work=0;
            let sum_carry=0;
            const outMines =room.controller.level>7? outMine:_.filter(outMine,e=>e.RoomType !=6);            
            for(const e of outMines)
            {
                 
                sum_work+= e.getWorkBodyNum();
                sum_carry += e.getCarryBodyNum();
            }
            const  Status = this.getStats([["mine",WORK,sum_work],["porter",CARRY,sum_carry]],room.controller.level);
            sum_bodyCount+=Status.totalbody;
        }
        const radio = Math.round(bodycount_limit/sum_bodyCount*100)/100-0.5;
        if(radio>1)
        {
            return 1;
        }
        else if(radio<0.7)
        {
            return 0.7;
        }
        return radio;
    }

    public makeTask(event: KeepEvent, region: string): IDecisionCode<T>|false
    {
        const room = Game.rooms[event.region] as Required<Room>;
        const powerconfig = this.getPowerConfig(room);
        
        const bodycount_limit = room.getEntity("SpawnEntity").spawnStructures().length*260*(powerconfig.radio*0.8>1?powerconfig.radio*0.8:1);
      
        const innerMine = room.getEntitys("InnerMineEntity");
        let outMine = room.getEntitys("OutMineEntity");
        const outMinerial = room.getEntitys("OutMinerialEntity");
        if(room.controller.level<7)
        {
            // 过滤掉除中央房的
            for(let i=0;i<outMine.length;i++)
            {
                if(UtilsHelper.isCenterRoom(outMine[i].roomName))
                {
                    outMine[i].updateState(1);
                    outMine.splice(i,1);
                    
                    i--;
                }
            }      
            outMinerial.forEach(e=>e.updateState(1));      
        }
        
        let keepTask = room.getTaskByType("KeepCreep")[0];// keep 只能作为统计信息查看。不能限制爬的数量
        let isnewTask = false;
        if(!keepTask)
        {
            keepTask =  {
                id:generateUniqueId(),
                priority:priority.S,
                ttl:-1,
                type:"KeepCreep",
                data:{
                    role: []
                },
                shard:0,
            }
            isnewTask = true;
           // throw new Error(`任务未初始化`);
        }
        // 1500T内 总能量收入
        let sum_income = 0;
        let sum_work = 0;
        let sum_carry=0;
        // 
        const porter_Radio = this.getPorterRadio(room,innerMine,outMine,bodycount_limit);
        for(const e of innerMine)
        {
            sum_income+=e.getEnergyHarvestSpeed()*1500;

            sum_work+= e.getWorkBodyNum();
            if(!e.Link)
            {
                // 没有Link的才需要运输爬
                sum_carry += e.getCarryBodyNum()*porter_Radio;
            }
            e.getNodeMemory().state=0;
        }
        const innerStatus = this.getStats([["mine",WORK,sum_work],["porter",CARRY,sum_carry]],room.controller.level);
        const status =[innerStatus];
        //global.Log.Info(`外矿数量统计 :${JSON.stringify(innerStatus)}`);
       
        const groupmines = this.sortOutMines(outMine);
        //global.Log.Debug(`${room.name}外矿排序`,`${groupmines.map(e=>e[0].roomName)}`);
        //global.Log.Info(`外矿分组数量统计 :${groupmines.length}`);
        let totalbodys =innerStatus.totalbody;
        let totalcost = innerStatus.totalcost;
        for(const g of groupmines)
        {
            //global.Log.Info(`已统计body数量:${totalbodys}`);
           
            let mine_work=0;
            let mine_carry=0;
            let sum_path = 0 ;
            const isNineRoom  = (g[0].RoomType == 6);
            for(const e of g)
            {
                sum_income+=e.getEnergyHarvestSpeed()*1500;
                mine_work+= e.getWorkBodyNum();
                if(UtilsHelper.isCenterRoom(e.roomName))
                {
                    mine_carry += e.getCarryBodyNum() ;
                }
                else 
                {
                    mine_carry += e.getCarryBodyNum()*porter_Radio ;
                }
                if(!isNineRoom)
                {
                    sum_path+=e.getPath().length;
                }
            }
            // 如果是9房外矿还要加上
            const outmineret = this.getStats([["mine",WORK,mine_work],["porter",CARRY,mine_carry]],room.controller.level);
            let tmptotalbodys = totalbodys+outmineret.totalbody;
            let tmptotalcost = totalcost+outmineret.totalcost;
            
            if(isNineRoom &&  UtilsHelper.isCenterRoom(g[0].roomName) ==1)
            {
                // 二人小队的开销
                tmptotalbodys += 100;
                tmptotalcost += 8000;
            }
            if(tmptotalbodys>bodycount_limit || (room.controller.level==8 && (sum_path/g.length)>100))
            {
                //global.Log.Debug(`${room.name}_超出限制body数量:`,`超出限制body数量:[可使用的body数量${totalbodys}]  ${tmptotalbodys}>${bodycount_limit}\r\n外矿统计:[${JSON.stringify(g.map(e=>e.BasePosition))}]\r\n[${JSON.stringify(outmineret)}]------------`);
                // 超出限制
                // break;
                for(const e of g)
                {
                    e.updateState(1);
                    //global.Log.Debug(`${room.name}关闭外矿${e.roomName}`,`${JSON.stringify(e.BasePosition)}`);
                    // 同时采集任务也需要清理掉
                }
                // 这个房间的矿物采集也要关掉
                outMinerial.filter(e=>e.roomName == g[0].roomName).forEach(m=>m.updateState(1));
            }
            else
            {
                for(const e of g)
                {
                    e.updateState(0);
                    //global.Log.Debug(`${room.name}启用外矿${e.roomName}`,`${JSON.stringify(e.BasePosition)}`);
                }
                outMinerial.filter(e=>e.roomName == g[0].roomName).forEach(m=>m.updateState(0));
                totalbodys =tmptotalbodys;
                totalcost = tmptotalcost;
                status.push(outmineret);
            }
        }
        this.makeRoomStats(room,sum_income,totalcost,totalbodys,powerconfig,porter_Radio);

        // const taskRoles:[IBodyConfig,number][] = [];
        for(const statu of status)
        {
            for(const e of statu.config)
            {
                let idx = keepTask.data.role.findIndex(i=>i[0].Role == e.role);
                if(idx>=0)
                {
                    keepTask.data.role[idx][0].body=  e.body;
                    keepTask.data.role[idx][1] +=   e.required;
                }
                else
                {
                    keepTask.data.role.push([{
                        Role:e.role,
                        body:e.body,
                        isAdapt:true,
                    },
                    e.required]);
                }
            }
        }
        if(room.terminal)
        {
            let idx = keepTask.data.role.findIndex(i=>i[0].Role == "manager");
            if(idx>=0)
            {
                keepTask.data.role[idx][0].body=  getRoleConfig("manager",room.controller.level).body;
                keepTask.data.role[idx][1] =   1;
            }
            else
            {
                keepTask.data.role.push([{Role:"manager",body:getRoleConfig("manager",room.controller.level).body},1])
            }
           
        }
        
        // global.Log.Debug(`Keep统计信息:${room.name}`,`增加外矿body数量${JSON.stringify( taskRoles )}  \r\n ${status.length}:\r\n${JSON.stringify(status)}`);
       
        // 计算房间状态
        if(isnewTask)
        {
            const code:IDecisionCode<T>={
                region:event.region,
                isunique:false,
                task:[keepTask],
            }
            return code
        }
        return false;
    }
    
}